/*
 * Copyright 2012-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot.util;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.core.ResolvableType;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

/**
 * 一个工具类，用于以安全的方式调用 Lambda。主要设计用于支持泛型类型的回调，
 * 因为类型擦除导致的 {@link ClassCastException 类转换异常} 需要被处理。
 *
 * @author Phillip Webb
 * @since 2.0.0
 */
public final class LambdaSafe {

	private static final Method CLASS_GET_MODULE;

	private static final Method MODULE_GET_NAME;

	static {
		CLASS_GET_MODULE = ReflectionUtils.findMethod(Class.class, "getModule");
		MODULE_GET_NAME = (CLASS_GET_MODULE != null)
				? ReflectionUtils.findMethod(CLASS_GET_MODULE.getReturnType(), "getName") : null;
	}

	private LambdaSafe() {
	}

	/**
	 * 开始调用单个回调实例，处理常见的泛型类型问题和异常。
	 * @param callbackType 回调类型（一个 {@link FunctionalInterface 函数式接口}）
	 * @param callbackInstance 回调实例（可能是 lambda）
	 * @param argument 传递给回调的主参数
	 * @param additionalArguments 传递给回调的其他参数
	 * @param <C> 回调类型
	 * @param <A> 主参数类型
	 * @return 一个可调用的 {@link Callback} 实例
	 */
	public static <C, A> Callback<C, A> callback(Class<C> callbackType, C callbackInstance, A argument,
			Object... additionalArguments) {
		Assert.notNull(callbackType, "CallbackType must not be null");
		Assert.notNull(callbackInstance, "CallbackInstance must not be null");
		return new Callback<>(callbackType, callbackInstance, argument, additionalArguments);
	}

	/**
	 * 开始调用多个回调实例，处理常见的泛型类型问题和异常。
	 * @param callbackType 回调类型（一个 {@link FunctionalInterface 函数式接口}）
	 * @param callbackInstances 回调实例集合（元素可能是 lambda）
	 * @param argument 传递给回调的主参数
	 * @param additionalArguments 传递给回调的其他参数
	 * @param <C> 回调类型
	 * @param <A> 主参数类型
	 * @return 一个可调用的 {@link Callbacks} 实例
	 */
	public static <C, A> Callbacks<C, A> callbacks(Class<C> callbackType, Collection<? extends C> callbackInstances,
			A argument, Object... additionalArguments) {
		Assert.notNull(callbackType, "CallbackType must not be null");
		Assert.notNull(callbackInstances, "CallbackInstances must not be null");
		return new Callbacks<>(callbackType, callbackInstances, argument, additionalArguments);
	}

	/**
	 * Lambda 安全回调的抽象基类。
	 *
	 * @param <C> 回调类型
	 * @param <A> 主参数类型
	 * @param <SELF> 自身类的引用类型
	 */
	protected abstract static class LambdaSafeCallback<C, A, SELF extends LambdaSafeCallback<C, A, SELF>> {

		private final Class<C> callbackType;

		private final A argument;

		private final Object[] additionalArguments;

		private Log logger;

		private Filter<C, A> filter = new GenericTypeFilter<>();

		LambdaSafeCallback(Class<C> callbackType, A argument, Object[] additionalArguments) {
			this.callbackType = callbackType;
			this.argument = argument;
			this.additionalArguments = additionalArguments;
			this.logger = LogFactory.getLog(callbackType);
		}

		/**
		 * 使用指定的日志源来报告任何 Lambda 失败。
		 * @param loggerSource 要使用的日志源
		 * @return 当前实例
		 */
		public SELF withLogger(Class<?> loggerSource) {
			return withLogger(LogFactory.getLog(loggerSource));
		}

		/**
		 * 使用指定的日志记录器来报告任何 Lambda 失败。
		 * @param logger 要使用的日志记录器
		 * @return 当前实例
		 */
		public SELF withLogger(Log logger) {
			Assert.notNull(logger, "Logger must not be null");
			this.logger = logger;
			return self();
		}

		/**
		 * 使用指定的过滤器来决定回调何时应用。如果没有显式设置过滤器，将尝试使用回调类型的泛型类型进行过滤。
		 * @param filter 要使用的过滤器
		 * @return 当前实例
		 */
		SELF withFilter(Filter<C, A> filter) {
			Assert.notNull(filter, "Filter must not be null");
			this.filter = filter;
			return self();
		}

		protected final <R> InvocationResult<R> invoke(C callbackInstance, Supplier<R> supplier) {
			if (this.filter.match(this.callbackType, callbackInstance, this.argument, this.additionalArguments)) {
				try {
					return InvocationResult.of(supplier.get());
				}
				catch (ClassCastException ex) {
					if (!isLambdaGenericProblem(ex)) {
						throw ex;
					}
					logNonMatchingType(callbackInstance, ex);
				}
			}
			return InvocationResult.noResult();
		}

		private boolean isLambdaGenericProblem(ClassCastException ex) {
			return (ex.getMessage() == null || startsWithArgumentClassName(ex.getMessage()));
		}

		private boolean startsWithArgumentClassName(String message) {
			Predicate<Object> startsWith = (argument) -> startsWithArgumentClassName(message, argument);
			return startsWith.test(this.argument) || Stream.of(this.additionalArguments).anyMatch(startsWith);
		}

		private boolean startsWithArgumentClassName(String message, Object argument) {
			if (argument == null) {
				return false;
			}
			Class<?> argumentType = argument.getClass();
			// 在 Java 8 中，异常信息以类名开头："java.lang.String cannot be cast..."
			if (message.startsWith(argumentType.getName())) {
				return true;
			}
			// 在 Java 11 中，异常信息以 "class ..." 开头，即 Class.toString() 的结果
			if (message.startsWith(argumentType.toString())) {
				return true;
			}
			// 在 Java 9 中，异常信息包含模块名：
			// "java.base/java.lang.String cannot be cast..."
			int moduleSeparatorIndex = message.indexOf('/');
			if (moduleSeparatorIndex != -1 && message.startsWith(argumentType.getName(), moduleSeparatorIndex + 1)) {
				return true;
			}
			if (CLASS_GET_MODULE != null) {
				Object module = ReflectionUtils.invokeMethod(CLASS_GET_MODULE, argumentType);
				Object moduleName = ReflectionUtils.invokeMethod(MODULE_GET_NAME, module);
				return message.startsWith(moduleName + "/" + argumentType.getName());
			}
			return false;
		}

		private void logNonMatchingType(C callback, ClassCastException ex) {
			if (this.logger.isDebugEnabled()) {
				Class<?> expectedType = ResolvableType.forClass(this.callbackType).resolveGeneric();
				String expectedTypeName = (expectedType != null) ? ClassUtils.getShortName(expectedType) + " type"
						: "type";
				String message = "Non-matching " + expectedTypeName + " for callback "
						+ ClassUtils.getShortName(this.callbackType) + ": " + callback;
				this.logger.debug(message, ex);
			}
		}

		@SuppressWarnings("unchecked")
		private SELF self() {
			return (SELF) this;
		}

	}

	/**
	 * 表示一个可以以安全 Lambda 方式调用的单个回调。
	 *
	 * @param <C> 回调类型
	 * @param <A> 主参数类型
	 */
	public static final class Callback<C, A> extends LambdaSafeCallback<C, A, Callback<C, A>> {

		private final C callbackInstance;

		private Callback(Class<C> callbackType, C callbackInstance, A argument, Object[] additionalArguments) {
			super(callbackType, argument, additionalArguments);
			this.callbackInstance = callbackInstance;
		}

		/**
		 * 调用回调实例，回调方法返回 void。
		 * @param invoker 用于调用回调的方法
		 */
		public void invoke(Consumer<C> invoker) {
			invoke(this.callbackInstance, () -> {
				invoker.accept(this.callbackInstance);
				return null;
			});
		}

		/**
		 * 调用回调实例，回调方法返回结果。
		 * @param invoker 用于调用回调的方法
		 * @param <R> 结果类型
		 * @return 调用结果（如果未调用回调，则可能为 {@link InvocationResult#noResult}）
		 */
		public <R> InvocationResult<R> invokeAnd(Function<C, R> invoker) {
			return invoke(this.callbackInstance, () -> invoker.apply(this.callbackInstance));
		}

	}

	/**
	 * 表示一组可以以安全的 Lambda 方式调用的回调集合。
	 *
	 * @param <C> 回调类型
	 * @param <A> 主参数类型
	 */
	public static final class Callbacks<C, A> extends LambdaSafeCallback<C, A, Callbacks<C, A>> {

		private final Collection<? extends C> callbackInstances;

		private Callbacks(Class<C> callbackType, Collection<? extends C> callbackInstances, A argument,
				Object[] additionalArguments) {
			super(callbackType, argument, additionalArguments);
			this.callbackInstances = callbackInstances;
		}

		/**
		 * 调用回调实例，回调方法返回 void。
		 * @param invoker 用于调用回调的方法
		 */
		public void invoke(Consumer<C> invoker) {
			this.callbackInstances.forEach((callbackInstance) -> invoke(callbackInstance, () -> {
				invoker.accept(callbackInstance);
				return null;
			}));
		}

		/**
		 * 调用回调实例，回调方法返回结果。
		 * @param invoker 用于调用回调的方法
		 * @param <R> 结果类型
		 * @return 调用结果的流（如果没有回调可调用，则可能为空流）
		 */
		public <R> Stream<R> invokeAnd(Function<C, R> invoker) {
			Function<C, InvocationResult<R>> mapper = (callbackInstance) -> invoke(callbackInstance,
					() -> invoker.apply(callbackInstance));
			return this.callbackInstances.stream().map(mapper).filter(InvocationResult::hasResult)
					.map(InvocationResult::get);
		}

	}

	/**
	 * 用于限制回调何时被使用的过滤器。
	 *
	 * @param <C> 回调类型
	 * @param <A> 主参数类型
	 */
	@FunctionalInterface
	interface Filter<C, A> {

		/**
		 * 判断给定的回调是否匹配并应被调用。
		 * @param callbackType 回调类型（函数式接口）
		 * @param callbackInstance 回调实例（实现类）
		 * @param argument 主参数
		 * @param additionalArguments 其他附加参数
		 * @return 回调是否匹配且应被调用
		 */
		boolean match(Class<C> callbackType, C callbackInstance, A argument, Object[] additionalArguments);

		/**
		 * 返回一个允许所有回调被调用的 {@link Filter}。
		 * @param <C> 回调类型
		 * @param <A> 主参数类型
		 * @return 一个“全部允许”的过滤器
		 */
		static <C, A> Filter<C, A> allowAll() {
			return (callbackType, callbackInstance, argument, additionalArguments) -> true;
		}

	}

	/**
	 * {@link Filter} 实现，当回调有单一泛型且主参数是该泛型的实例时匹配。
	 */
	private static class GenericTypeFilter<C, A> implements Filter<C, A> {

		@Override
		public boolean match(Class<C> callbackType, C callbackInstance, A argument, Object[] additionalArguments) {
			ResolvableType type = ResolvableType.forClass(callbackType, callbackInstance.getClass());
			if (type.getGenerics().length == 1 && type.resolveGeneric() != null) {
				return type.resolveGeneric().isInstance(argument);
			}

			return true;
		}

	}

	/**
	 * 回调的结果，可能是一个值、{@code null}，或者如果回调不合适则完全不存在。
	 * 设计上类似于 {@link Optional}，但允许 {@code null} 作为有效值。
	 *
	 * @param <R> 结果类型
	 */
	public static final class InvocationResult<R> {

		private static final InvocationResult<?> NONE = new InvocationResult<>(null);

		private final R value;

		private InvocationResult(R value) {
			this.value = value;
		}

		/**
		 * 如果结果存在则返回 true。
		 * @return 是否存在结果
		 */
		public boolean hasResult() {
			return this != NONE;
		}

		/**
		 * 返回调用结果；如果回调不合适，则返回 {@code null}。
		 * @return 调用结果或 {@code null}
		 */
		public R get() {
			return this.value;
		}

		/**
		 * 返回调用结果；如果回调不合适，则返回指定的备用值。
		 * @param fallback 当没有结果时使用的备用值
		 * @return 调用结果或备用值
		 */
		public R get(R fallback) {
			return (this != NONE) ? this.value : fallback;
		}

		/**
		 * 创建一个带有指定值的新 {@link InvocationResult} 实例。
		 * @param value 值（可以为 {@code null}）
		 * @param <R> 结果类型
		 * @return 一个 {@link InvocationResult} 实例
		 */
		public static <R> InvocationResult<R> of(R value) {
			return new InvocationResult<>(value);
		}

		/**
		 * 返回一个表示无结果的 {@link InvocationResult} 实例。
		 * @param <R> 结果类型
		 * @return 一个 {@link InvocationResult} 实例
		 */
		@SuppressWarnings("unchecked")
		public static <R> InvocationResult<R> noResult() {
			return (InvocationResult<R>) NONE;
		}

	}

}
